#include "audioengine.h"
#include <qapplication.h>


#include <QByteArray>
#include <QDebug>
#include <QObject>
#include <windows.h>

AudioEngine::AudioEngine()
    :
    m_streamIn(0),
    m_streamOut(0),
    m_errIn(paNoError),
    m_errOut(paNoError),
    m_inputId(-1),
    m_outputId(-1)
{

    m_data.obj = this;
    m_data.speexdec.setSanSpeexEnc(&m_data.speexenc);
    initPa();
}

AudioEngine::~AudioEngine()
{
    unInitPa();
}

void AudioEngine::closeEvent(QCloseEvent *)
{
    qApp->exit(0);
}

int AudioEngine::s_recordCallback(const void *inputBuffer, void *outputBuffer,
                                  unsigned long framesPerBuffer,
                                  const PaStreamCallbackTimeInfo* timeInfo,
                                  PaStreamCallbackFlags statusFlags,
                                  void *userData)
{
//      qDebug("r");

    paTestData *data = (paTestData*)userData;
    const SAMPLE *rptr = (const SAMPLE*)inputBuffer;
    SAMPLE *wptr = data->recordbuffer;
    long framesToCalc;
    framesToCalc = framesPerBuffer;
    long i;
    int finished = paContinue;

    (void) outputBuffer;
    (void) timeInfo;
    (void) statusFlags;
    (void) userData;

    if(inputBuffer == NULL)
    {
        for(i = 0; i < framesToCalc; i++)
        {
            *wptr++ = SAMPLE_SILENCE;
            if (NUM_CHANNELS == 2)
                *wptr++ = SAMPLE_SILENCE;
        }
    }
    else
    {
        for( i = 0; i < framesToCalc; i++ )
        {
            *wptr++ = *rptr++;
            if (NUM_CHANNELS == 2)
                *wptr++ = *rptr++;
        }
    }
    int encSizePlus8 = data->speexenc.encode(data->recordbuffer, data->encodedBufPlus8);
    if (encSizePlus8 > 0)
    {
        data->obj->sendRecordDataOut(data->encodedBufPlus8, encSizePlus8);
    }
    return finished;
}

int AudioEngine::s_playCallback(const void *inputBuffer, void *outputBuffer,
                                unsigned long framesPerBuffer,
                                const PaStreamCallbackTimeInfo* timeInfo,
                                PaStreamCallbackFlags statusFlags,
                                void *userData)
{
    paTestData *data = (paTestData*)userData;
    SAMPLE *rptr = data->playbackbuffer;
    SAMPLE *wptr = (SAMPLE*)outputBuffer;
    int finished = paContinue;
    unsigned int framesLeft = 0;
    data->speexdec.get(rptr);
    framesLeft = framesPerBuffer;

    (void) inputBuffer;
    (void) timeInfo;
    (void) statusFlags;
    (void) userData;

    for(unsigned long i = 0; i < framesPerBuffer; i++ )
    {
        *wptr++ = *rptr++;
        if (NUM_CHANNELS == 2)
            *wptr++ = *rptr++;
    }
    rptr = data->playbackbuffer;
    data->speexdec.afterPlayback(rptr, true);
    return finished;
}

bool AudioEngine::initPa()
{
    PaError err = Pa_Initialize();
    if (err == paNoError)
    {
        m_inputParameters.device = Pa_GetDefaultInputDevice();
        if (m_inputParameters.device == paNoDevice)
            qDebug("Error Pa_GetDefaultInputDevice()\n");
        m_inputParameters.channelCount = NUM_CHANNELS;
        m_inputParameters.sampleFormat = PA_SAMPLE_TYPE;
        m_inputParameters.suggestedLatency = Pa_GetDeviceInfo( m_inputParameters.device )->defaultHighInputLatency ;
        m_inputParameters.hostApiSpecificStreamInfo = NULL;

        m_outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
        if (m_outputParameters.device == paNoDevice)
            qDebug("Error Pa_GetDefaultOutputDevice()\n");
        m_outputParameters.channelCount = NUM_CHANNELS;
        m_outputParameters.sampleFormat = PA_SAMPLE_TYPE;
        m_outputParameters.suggestedLatency = Pa_GetDeviceInfo( m_outputParameters.device )->defaultHighOutputLatency;
        m_outputParameters.hostApiSpecificStreamInfo = NULL;
    }
    else
        return err;
    qDebug("===============init complete=======================");
    return (err);
}

bool AudioEngine::unInitPa()
{
    if (m_streamIn)
        Pa_CloseStream(m_streamIn);
    m_streamIn = 0;
    if (m_streamOut)
        Pa_CloseStream(m_streamOut);
    m_streamOut = 0;

    Pa_Terminate();
    qDebug("===============uninit complete=======================");
    return true;
}

bool AudioEngine::startPaRecorder()
{
    qDebug("Input Device = [%s], latency = %f", Pa_GetDeviceInfo(m_inputParameters.device)->name, m_inputParameters.suggestedLatency);
    m_errIn = Pa_OpenStream(&m_streamIn,
                            &m_inputParameters,
                            NULL,
                            SAMPLE_RATE,
                            FRAMES_PER_BUFFER*NUM_CHANNELS,
                            paClipOff|paDitherOff,
                            AudioEngine::s_recordCallback,
                            &m_data);
    if(m_errIn != paNoError)
        return false;
    m_errIn = Pa_StartStream(m_streamIn);
    if(m_errIn != paNoError)
        return false;
    qDebug("Now recording!!");
    return true;
}

bool AudioEngine::startPaPlayback()
{
    qDebug("Output Device = [%s], latency = %f", Pa_GetDeviceInfo(m_outputParameters.device)->name, m_outputParameters.suggestedLatency);
    qDebug("Begin playback.");
    m_errOut = Pa_OpenStream(&m_streamOut,
                             NULL, /* no input */
                             &m_outputParameters,
                             SAMPLE_RATE,
                             FRAMES_PER_BUFFER*NUM_CHANNELS,
                             paClipOff|paDitherOff,
                             AudioEngine::s_playCallback,
                             &m_data);
    if(m_errOut != paNoError)
        return false;

    m_errOut = Pa_StartStream(m_streamOut);
    if(m_errOut != paNoError )
        return false;
    return true;
}

bool AudioEngine::stopPaRecorder()
{
    if (m_streamIn)
        Pa_CloseStream(m_streamIn);
    m_streamIn = 0;
    return true;
}

bool AudioEngine::stopPaPlayback()
{
    if (m_streamOut)
        Pa_CloseStream(m_streamOut);
    m_streamOut = 0;
    return true;
}

void AudioEngine::sendRecordDataOut(char *buf, int len)
{
    QByteArray *ba = new QByteArray;
    ba->setRawData(buf, len);
    emit senddata(ba);

    // ba->clear();

    // ba A DELETE
}

void    AudioEngine::recv_data(QByteArray* ba)
{
    recvPlaybackDataIn(ba);
}

void AudioEngine::recvPlaybackDataIn(QByteArray* ba)
{
    int len = 0;
    if ((len = ba->size()) > 8)
    {
        char *buf = new char [len];
        memcpy(buf, ba->data(), len);
        m_data.speexdec.put(buf+4, len-4);
        delete [] buf;
        buf = 0;
    }
}
